home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sound Fx
/
Sound Fx.iso
/
Software
/
UNZIPED
/
SBPLY254
/
SOURCE.ZIP
/
_SBDMA.ASM
< prev
next >
Wrap
Assembly Source File
|
1996-12-05
|
29KB
|
1,373 lines
_TEXT SEGMENT WORD PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT WORD PUBLIC 'DATA'
_DATA ENDS
CONST SEGMENT WORD PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT WORD PUBLIC 'BSS'
;GLOBAL UNINITIATED DATA GOES HERE
_BSS ENDS
DGROUP GROUP CONST,_BSS,_DATA
;
ASSUME DS:DGROUP,SS:DGROUP
;EXTRN EXTERNAL SUBROUTINE CALLS GO HERE
;
;
; _sbdma FAR Procedure for C Written by John A. Ball December 5, 1996
;
; Copyright (c) 1996 John A. Ball
;
; You may copy and modify for your own use only!
; Please notify me of any errors or suggested enhancements.
;
; Plays back sound on the Sound Blaster or Sound Blaster Pro
;
; int error sbdma(char *buffer, unsigned number_read,unsigned frequency,
; unsigned voc_pack);
;
; error=sbdma(Buffer Pointer,Length,Frequency,voc_pack,Volume,channels);
;
; Buffer Pointer is a Far pointer to the sound buffer (64k max)
; Length is the number of sound samples to play
; Frequency is the playback frequency in hertz
; voc_pack is the DSP dma mode
; Volume is the sound volume for SB Pro cards and above
; error=0 ok
; error 1=DMA in use
; error 2=IRQ/DMA not Found
; error 3=DSP not responding
; error 4=No DSP version
; error 5=DMA channel not correct
; error 6=IRQ in environment not correct
; error 7=IRQ not valid for Sound Blaster
; error 8=Number of samples is zero
; error -1=Sound Halted
;
_TEXT SEGMENT
ASSUME CS:_TEXT
PUBLIC _sbdma,_sb_info,_sbdelay
_sbdma PROC FAR
PARMA EQU [BP+6] ;Sound Buffer Pointer
PARMB EQU [BP+10] ;Number of samples to play (Length)
PARMC EQU [BP+12] ;Frequency to playback samples
PARMD EQU [BP+14] ;VOC Pack mode determines DSP DMA Mode
PARME EQU [BP+16] ;Volume for playback
PARMF EQU [BP+18] ;Number of 8 bit channels
PUSH BP ;Save stack Frame
MOV BP,SP
PUSHF
PUSH DS ;Save registers
PUSH ES
PUSH DI
MOV AX,DGROUP ;Set DS = our data segment
MOV DS,AX
JMP OVERDATA
_sb_info LABEL WORD
DMAFLAG DW 0 ;Flag to indicate DMA for DAC in use
SB_IRQ DW -1 ;Sound Blaster IRQ (default 5)
TIME_CONST DW 0 ;DSP Time constant
SAMPLES DW 0 ;Number of samples to play
SB_IO_ADDR DW 0 ;Sound Blaster I/O Address
SB_VER DW 0 ;Sound Blaster DSP Version
SB_DMA DW 1 ;Sound Blaster DMA Channel
SB_TYPE DW 1 ;Sound Blaster board type
ERROR DW 0 ;Sound Blaster Hardware Error
VOLUME DW 0 ;Sound Blaster Playback Volume
STEREO DW 0 ;Sound Blaster mono or stereo mode
PRE_MODE DW -1 ;Previous mono stereo mode
OUT_FILTER DW 0 ;Status of output filter
IN_DV DW 0 ;In DESQview Flag
TEMP DW 0 ;Temporary Storage
BUFFER LABEL WORD
BUFFER_ADDR DD 0 ;Pointer to Sound Data
OLD_INTERRUPT LABEL WORD
OLD_INTERRUPT_ADDR DD 0 ;Location of previous IRQ 5 System Interrupt
SAVEPIC DB 0 ;Contents of PIC 1
SAVEPIC2 DB 0 ;Contents of PIC 2
DMA_MODE DB 014H ;DSP DMA mode
DSP_SPEED DB 0 ;Indicates whether highspeed dma is required
SB_TEST DB 0 ;Indicates whether SB has been tested
IRQ DW 0 ;IRQ from environment
TESTBUFFER DB 1 DUP(080H)
OLD_ISR2 LABEL WORD
OLD_ISR2_ADDR DD 0
OLD_ISR3 LABEL WORD
OLD_ISR3_ADDR DD 0
OLD_ISR5 LABEL WORD
OLD_ISR5_ADDR DD 0
OLD_ISR7 LABEL WORD
OLD_ISR7_ADDR DD 0
OLD_ISR10 LABEL WORD
OLD_ISR10_ADDR DD 0
DSP_DMA_MODE DB 014H, 075H, 077H, 017H, 0C0H, 0C0H, 0B0H, 0B0H
DMA_PAGE DB 087H, 083H, 081H, 082H
OVERDATA:
CMP CS:DMAFLAG,0 ;Is DMAFLAG for DAC still in Use?
JZ OK
MOV CS:ERROR,1 ;DMA in use error
JMP EXIT2
OK:
MOV AX,0 ;Reset error
MOV CS:ERROR,AX
CMP CS:SB_IRQ,-1 ;See if IRQ was available
JNE TEST_SB
CALL SBFINDIRQ ;Find IRQ if none given
CMP CS:ERROR,0 ;Check for errors
JE TEST_SB
JMP EXIT2
TEST_SB:
CMP CS:SB_TEST,0 ;Has SB been tested?
JNE OK1
CALL FIND_DV ;See if in Desqview?
MOV AX,CS:SB_IRQ ;Get IRQ from environment
MOV CS:IRQ,AX ;and save it for later
CALL SBFINDIRQ ;Test IRQ/DMA operation
CMP CS:ERROR,0 ;Check to see if IRQ was found OK
JE IRQ_FOUND
JMP EXIT2
IRQ_FOUND:
CMP CS:IRQ,0 ;See if IRQ was available
JE SBOK
MOV AX,CS:IRQ ;if it was
CMP AX,CS:SB_IRQ ;see if it is the same as the IRQ found
JE SBOK
MOV CS:ERROR,6 ;indicate wrong IRQ found in environment
SBOK: MOV CS:SB_TEST,1
CMP CS:ERROR,0 ;Check to see if IRQ was found ok?
JE OK1
JMP EXIT2
OK1:
CMP CS:SB_TYPE,-1 ;If SB is type -1
JE OK2 ;don't check dsp version
CMP CS:SB_VER,00
JNZ OK2
CALL GET_DSP_VER ;Check DSP version for maximum playback
OK2: LES DI,PARMA ;GET SOUND BUFFER POINTER
MOV CS:BUFFER,DI ;AND SAVE
MOV DI,ES
MOV CS:BUFFER[2],DI
MOV BX,PARMB ;Get number of samples
CMP BX,0 ;Check for zero samples
JNE SAVE_S
MOV CS:ERROR,8
SAVE_S: MOV CS:SAMPLES,BX ;AND SAVE
MOV AX,PARMF ;Get number of sound channels
AND AX,02H ;Limit to mono or stereo
MOV CS:STEREO,AX
MOV BX,PARMC ;Get frequency & calculate time constant
CMP CS:STEREO,2 ;Half frequency for sb16
JNE FREQOK
CMP CS:SB_VER,0400H
JL FREQOK
SHR BX,1
FREQOK: MOV DX,000FH
MOV AX,04240H
DIV BX
XOR AH,AH
SUB AH,AL
MOV AL,AH
XOR AH,AH
MOV CS:TIME_CONST,AX
CMP AL,0EAH ;Check playback speed
JBE L18
MOV AX,00EAH ;Limit playback speed to 44,100 hz
MOV CS:TIME_CONST,AX
L18: CMP CS:SB_VER,0400H ;Skip high speed mode for SB16
JGE L24
MOV CS:DSP_SPEED,01
L24: CMP CS:SB_VER,0200H
JAE L22
CMP CS:TIME_CONST,0D4H ;Is playback greater than 22,727 hz?
JBE L22
MOV AX,00D4H ;Limit to 22,727 hz if DSP version < 2.00
MOV CS:TIME_CONST,AX
L22: CMP CS:TIME_CONST,0D4H ;Is playback less than 22,727 hz?
JA L19
MOV CS:DSP_SPEED,00
L19:
MOV AX,PARMD ;DSP DMA mode
CMP CS:SB_VER,0400H ;Limit to 4 modes if not SB16
JGE L33
AND AX,03 ;Limit to 4 modes
L33: AND AX,07 ;Limit to 8 modes for SB16
MOV BX,AX
MOV AL,DSP_DMA_MODE[BX]
MOV CS:DMA_MODE,AL
MOV AX,PARME ;Mixer sound volume
MOV DX,0
MOV BX,13
DIV BX
SHL AX,1
MOV CS:VOLUME,AX
CALL SET_VOLUME
CALL INSTALL_ISR ;Install IRQ for Sound Blaster
CALL SET_MODE ;Set mono or stereo
MOV DX,CS:SB_IO_ADDR
ADD DX,0CH
MOV AL,040H ;Set DSP time constant
CALL sbdspw
JNC A1
MOV CS:ERROR,3 ;DSP Error
JMP EXIT
A1: MOV AX,CS:TIME_CONST
CALL sbdspw
JNC A2
MOV CS:ERROR,3 ;DSP Error
JMP EXIT
A2:
INC CS:DMAFLAG ;SET DMAFLAG TO SHOW IN USE
MOV DX,CS:BUFFER[2] ;Convert Buffer Address to 64k Pages & Offset
MOV AX,CS:BUFFER
CALL PNT_TO_DMA
MOV CS:BUFFER,AX ;AND SAVE
MOV CS:BUFFER[2],DX
CALL START_DMA
CALL IO_WAIT ;Wait for IRQ from Sound Blaster
EXIT:
CALL RESTORE_ISR ;Restore IRQ & PIC
EXIT2: MOV AX,CS:ERROR ;Get error if any
POP DI ;Restore registers
POP ES
POP DS
POPF
POP BP ;RESTORE BP
RET ;RETURN FAR
HANDLER: ;Interrupt for IRQ comes here!
PUSHF
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH DS
PUSH ES
sti ;enable interrupts or computer will crash
CALL sbdsprt
CMP CS:SB_IRQ,10
JE ACKIRQ10
MOV AL,020H
OUT 020H,AL
JMP DONE_ACK
ACKIRQ10:
MOV AL,020H
OUT 020H,AL
OUT 0A0H,AL
DONE_ACK:
CMP CS:SAMPLES,0 ;Anymore samples?
JE L13
INC CS:BUFFER[2] ;Increment to next page
MOV CS:BUFFER,0000 ;and set offset to zero
CALL START_DMA
JMP EXIT1
L13: DEC CS:DMAFLAG ;Indicate it is
EXIT1: POP ES
POP DS
POP DX
POP CX
POP BX
POP AX
POPF
IRET
_sbdma ENDP
sbdspwt PROC NEAR
;Write to DSP with timeout
PUSH CX
MOV CX,200H
MOV AH,AL
L1: IN AL,DX
JMP $+2
OR AL,AL
JNS L2
LOOP L1
STC
JMP L3
L2: MOV AL,AH
OUT DX,AL
CLC
L3: POP CX
RET
sbdspwt ENDP
sbdspr PROC NEAR
;Read DSP
PUSH DX
MOV DX,CS:SB_IO_ADDR
ADD DL,0EH
SUB AL,AL
L7: IN AL,DX
JMP $+2
OR AL,AL
JNS L7
SUB DL,04H
IN AL,DX
POP DX
RET
sbdspr ENDP
sbdsprt PROC NEAR
;Read DSP with timeout
PUSH CX
PUSH DX
MOV DX,CS:SB_IO_ADDR
ADD DL,0EH
MOV CX,0200H
L4: IN AL,DX
JMP $+2
OR AL,AL
JS L5
LOOP L4
STC
JMP L6
L5: SUB DL,04H
IN AL,DX
CLC
L6: POP DX
POP CX
RET
sbdsprt ENDP
;Write to DSP
sbdspw PROC NEAR
PUSH CX
PUSH BX
MOV AH,AL
MOV BX,10
MOV CX,0ffffH ;After timeout send value anyway
CLC
L8: IN AL,DX
JMP $+2
JMP $+2
OR AL,AL
JNS L8E
LOOP L8
DEC BX
OR BX,BX
JNZ L8
STC ;Set Carry to indicate timeout
L8E: MOV AL,AH
OUT DX,AL
POP BX
POP CX
RET
sbdspw ENDP
;Convert FAR pointer to 64k pages and offset
PNT_TO_DMA PROC NEAR
PUSH CX
MOV CL,4
ROL DX,CL
MOV CX,DX
AND DX,0FH
AND CX,0FFF0H
ADD AX,CX
ADC DX,0
POP CX
RET
PNT_TO_DMA ENDP
INSTALL_ISR PROC NEAR
pushf
push ds
push es
CLI
MOV AX,CS:SB_IRQ
CMP AX,02
JE INSTALL
CMP AX,03
JE INSTALL
CMP AX,05
JE INSTALL
CMP AX,07
JE INSTALL
CMP AX,10
JE INSTALL
MOV CS:ERROR,7
MOV AX,5 ;Use default if error
INSTALL:
CALL IRQ_VECT
MOV AH,035H ;GET INTERRUPT for IRQ 5 AND SAVE IT
INT 21H
CLI
MOV CS:OLD_INTERRUPT,BX
MOV CS:OLD_INTERRUPT[2],ES
MOV AX,CS
MOV DS,AX
MOV AX,CS:SB_IRQ
CALL IRQ_VECT
MOV AH,025H ;AND SET NEW INTERRUPT IRQ 5
LEA DX,HANDLER
INT 21H
pop es
pop ds
popf
MOV AX,CS:SB_IRQ ;IRQ 5
AND AL,08H
JZ L11
IN AL,021H ;ENABLE IRQ from PIC
MOV CS:SAVEPIC,AL
AND AL,0FBH
OUT 021H,AL
MOV DX,00A1H
JMP L12
L11: MOV DX,021H
L12: MOV CX,CS:SB_IRQ ;Sound Blaster IRQ
AND CL,07H
MOV AH,01
SHL AH,CL
NOT AH
IN AL,DX
MOV CS:SAVEPIC2,AL
AND AL,AH
OUT DX,AL
RET
INSTALL_ISR ENDP
RESTORE_ISR PROC NEAR
;
;Restore IRQ and PICs before exiting
;
PUSH AX
MOV AX,0
MOV CS:DMAFLAG,AX ;Indicate DMA done
LDS DX,CS:OLD_INTERRUPT_ADDR
MOV AX,CS:SB_IRQ
CALL IRQ_VECT
CLI
MOV AH,025H ;Restore old IRQ 5
INT 21h
CLI
MOV AX,CS:SB_IRQ ;Restore PICs
AND AL,08
JZ L31
MOV AL,CS:SAVEPIC
OUT 021H,AL
MOV DX,00A1H
JMP L32
L31: MOV DX,0021H
L32: MOV AL,CS:SAVEPIC2
OUT DX,AL
POP AX
RET
RESTORE_ISR ENDP
IO_WAIT PROC NEAR
;
; Gives up time slice if time and stops dma if ESC key hit
;
IOWAIT1:
CMP CS:DMAFLAG,0 ;Is DMAFLAG for DAC still in Use?
JE DMA_DONE ;Wait until done
MOV AH,01H ;See if key pressed
INT 16H
JZ GET_DMA
KEY_PRESS:
MOV AX,0
INT 16H ;Get the pressed key
CMP AL,1BH ;See if ESC key hit?
JNE GET_DMA
CALL HALT_DMA
CMP CS:ERROR,0 ;Check for error when halting DMA
JNE EXIT_IO
MOV CS:ERROR,0FFFFH ;EXIT ERROR CODE
JMP EXIT_IO
; MOV CS:DMAFLAG,0 ;Reset DMAFLAG to show done
; MOV AX,-1 ;and indicate sound halted
JMP EXIT_IO
GET_DMA:
MOV AX,CS:SB_DMA ;Determine DMA count register
SHL AX,1
ADD AX,1
MOV DX,AX
IN AL,DX ;Get DMA countdown
MOV AH,AL
IN AL,DX
XCHG AL,AH
CMP AX,5000 ;If greater than 5000 bytes to go
JG GIVE_CPU ;then give up some cpu time
WAIT_HERE:
CMP CS:DMAFLAG,0 ;Is DMAFLAG for DAC still in Use?
JE DMA_DONE ;Wait until done
JMP WAIT_HERE
GIVE_CPU:
CMP CS:DMAFLAG,0 ;Is DMAFLAG for DAC still in Use?
JE DMA_DONE ;Wait until done
CALL GIVE_SLICE ;Give some time up
JMP IOWAIT1
DMA_DONE:
MOV AX,0 ;OK
EXIT_IO:
RET
IO_WAIT ENDP
; Start DMA transfer
START_DMA PROC NEAR
MOV AX,CS:SB_DMA
AND AX,03H ;Limit DMA channels to (0-3) 8-BIT
MOV CS:SB_DMA,AX
MOV AX,CS:SB_DMA
ADD AL,04
OUT 0AH,AL ;Set mask bit channel 1
JMP $+2
MOV AL,0FFH
OUT 0CH,AL ;Clear byte pointer flip/flop
JMP $+2
MOV AL,048H ;Write mode
ADD AX,CS:SB_DMA ;plus DMA channel
OUT 0BH,AL ;Single mode select read channel 1
JMP $+2
MOV BX,CS:SB_DMA ;Get DMA Page Register
MOV DX,0
MOV DL,CS:DMA_PAGE[BX]
MOV AX,CS:BUFFER[2]
OUT DX,AL ;Set DMA page register
JMP $+2
MOV BX,CS:BUFFER
MOV DX,CS:SB_DMA ;Determine memory address register
SHL DX,1
MOV AL,BL
OUT DX,AL ;Set base and current address
JMP $+2
MOV AL,BH
OUT DX,AL
JMP $+2
XOR BX,-1 ;(65536-bx) = #bytes that can be transfered
MOV AX,CS:SAMPLES ;Get # of samples & subtract 1
DEC AX ; # SAMPLES - 1
CMP AX,BX ;# SAMPLES > # that can be DMA?
JBE L10
MOV AX,BX ;If yes, use # that can be DMA
L10:
CLI
PUSH AX
INC DX
OUT DX,AL ;Base & current word count
JMP $+2
MOV AL,AH
OUT DX,AL
JMP $+2
POP AX
STI
PUSH AX ;Save number of samples to play
INC AX ;restore count
SUB CS:SAMPLES,AX ;Subtract # that can be DMA
MOV AX,CS:SB_DMA
OUT 0AH,AL ;Clear channel 1 mask bit to start DMA
MOV DX,CS:SB_IO_ADDR
ADD DX,0CH
CMP CS:DSP_SPEED,01 ;Check for high speed mode
JNZ L21
MOV AL,048H
CALL sbdspw
JNC A11
MOV CS:ERROR,3 ;DSP not responding
JMP L20
A11:
POP AX ;Get number of samples & send to DSP
MOV BX,AX
CALL sbdspw
JNC A12
MOV CS:ERROR,3 ;DSP not responding
JMP L20
A12:
MOV AL,BH
CALL sbdspw
JNC A13
MOV CS:ERROR,3 ;DSP not responding
JMP L20
A13:
MOV AL,091H ;High Speed DMA mode
CALL SBDSPW
JNC A14
MOV CS:ERROR,3 ;DSP not responding
JMP L20
A14:
JMP L20
L21:
MOV AL,CS:DMA_MODE ;DSP DMA mode for 8-bit DAC
CALL sbdspw
JNC A15
MOV CS:ERROR,3 ;DSP not responding
JMP L20
A15:
CMP CS:DMA_MODE,0C0H ;SB16 stereo?
JNE A15A
MOV AX,020H
CALL sbdspw
JNC A15A
MOV CS:ERROR,3 ;DSP not responding
JMP L20
A15A: POP AX ;Get number of samples & send to DSP
MOV BX,AX
CALL sbdspw
JNC A16
MOV CS:ERROR,3 ;DSP not responding
JMP L20
A16:
MOV AL,BH
CALL sbdspw
JNC L20
MOV CS:ERROR,3 ;DSP not responding
L20: RET
START_DMA ENDP
; Calculate interrupt vector for Sound Blaster IRQ (5)
IRQ_VECT PROC NEAR
PUSH BX
MOV BL,AL
AND BL,08
JZ L14
AND AL,07
ADD AL,070H
JMP L15
L14: ADD AL,08
L15: CBW
POP BX
RET
IRQ_VECT ENDP
SBFINDIRQ PROC NEAR
; Install ISRs for possible Sound Blaster IRQs
PUSHF
PUSH DX
PUSH CX
MOV AX,CS
MOV DS,AX
MOV ES,AX
MOV AL,02 ;IRQ 2
CALL IRQ_VECT ;Get IRQ ISR address
PUSH AX
MOV AH,035H ;Get old ISR address and save
INT 21H
MOV CS:OLD_ISR2,BX
MOV CS:OLD_ISR2[2],ES
MOV AX,CS
MOV DS,AX
MOV ES,AX
POP AX
CLI
MOV AH,025H ;AND SET NEW INTERRUPT IRQ 2
LEA DX,HANDLER2
INT 21H
MOV AL,03 ;IRQ 3
CALL IRQ_VECT
PUSH AX
MOV AH,035H ;Get old ISR address and save
INT 21H
MOV CS:OLD_ISR3,BX
MOV CS:OLD_ISR3[2],ES
MOV AX,CS
MOV DS,AX
MOV ES,AX
POP AX
CLI
MOV AH,025H ;AND SET NEW INTERRUPT IRQ 3
LEA DX,HANDLER3
INT 21H
MOV AL,05 ;IRQ 5
CALL IRQ_VECT
PUSH AX
MOV AH,035H ;Get old ISR address and save
INT 21H
MOV CS:OLD_ISR5,BX
MOV CS:OLD_ISR5[2],ES
MOV AX,CS
MOV DS,AX
MOV ES,AX
POP AX
CLI
MOV AH,025H ;AND SET NEW INTERRUPT IRQ 5
LEA DX,HANDLER5
INT 21H
MOV AL,07 ;IRQ 7
CALL IRQ_VECT
PUSH AX
MOV AH,035H ;Get old ISR address and save
INT 21H
MOV CS:OLD_ISR7,BX
MOV CS:OLD_ISR7[2],ES
MOV AX,CS
MOV DS,AX
MOV ES,AX
POP AX
CLI
MOV AH,025H ;AND SET NEW INTERRUPT IRQ 7
LEA DX,HANDLER7
INT 21H
MOV AL,10 ;IRQ 10
CALL IRQ_VECT
PUSH AX
MOV AH,035H ;Get old ISR address and save
INT 21H
MOV CS:OLD_ISR10,BX
MOV CS:OLD_ISR10[2],ES
MOV AX,CS
MOV DS,AX
MOV ES,AX
POP AX
CLI
MOV AH,025H ;AND SET NEW INTERRUPT IRQ 10
LEA DX,HANDLER10
INT 21H
IN AL,021H ;ENABLE IRQS from PIC1
MOV CS:SAVEPIC,AL
AND AL,053H
OUT 021H,AL
IN AL,0A1H ;ENABLE IRQS from PIC2
MOV CS:SAVEPIC2,AL
AND AL,0FBH
OUT 0A1H,AL
MOV CS:SB_IRQ,0 ;Set IRQ to 0 and see if it changes
CALL TEST_DMA ;Start short DMA and see if it works
CMP CS:ERROR,0
JNE FOUNDIRQ
MOV CX,0ffffh
IRQWAIT:
CALL GIVE_SLICE ;Give some time up
MOV AX,CS:SB_IRQ ;Wait for interrupt to occur
CMP AX,02 ;with Time-Out
JE FOUNDIRQ
CMP AX,03
JE FOUNDIRQ
CMP AX,05
JE FOUNDIRQ
CMP AX,07
JE FOUNDIRQ
CMP AX,10
JE FOUNDIRQ
LOOP IRQWAIT
IN AL,08H ;Read DMA status register
JMP $+2
IN AL,08H ;and again
CMP AL,0 ;Is status all clear?
JE IRQERR ;If 0 then IRQ error
MOV CS:ERROR,5 ;DMA error
MOV DX,CS:SB_IO_ADDR
ADD DX,0CH
MOV AL,080H
OUT DX,AL ;Send 1 byte to fix DSP
MOV CX,0ffffh ;Wait for interrupt
IRQWAIT1:
CALL GIVE_SLICE ;Give some time up
MOV AX,CS:SB_IRQ ;Wait for interrupt to occur
CMP AX,02 ;with Time-Out
JE FOUNDIRQ
CMP AX,03
JE FOUNDIRQ
CMP AX,05
JE FOUNDIRQ
CMP AX,07
JE FOUNDIRQ
CMP AX,10
JE FOUNDIRQ
LOOP IRQWAIT1
IRQERR: MOV CS:ERROR,2 ;IRQ error (IRQ did not occur)
FOUNDIRQ:
CLI
MOV AL,CS:SAVEPIC ;Restore IRQs as found
OUT 021H,AL
MOV AL,CS:SAVEPIC2
OUT 0A1H,AL
MOV AL,02 ;IRQ 2
CALL IRQ_VECT
CLI
MOV AH,025H
LDS DX,CS:OLD_ISR2_ADDR
INT 21H ;Restore OLD vector
MOV AL,03 ;IRQ 3
CALL IRQ_VECT
CLI
MOV AH,025H
LDS DX,CS:OLD_ISR3_ADDR
INT 21H ;Restore OLD vector
MOV AL,05 ;IRQ 5
CALL IRQ_VECT
CLI
MOV AH,025H
LDS DX,CS:OLD_ISR5_ADDR
INT 21H ;Restore OLD vector
MOV AL,07 ;IRQ 7
CALL IRQ_VECT
CLI
MOV AH,025H
LDS DX,CS:OLD_ISR7_ADDR
INT 21H ;Restore OLD vector
MOV AL,10 ;IRQ 10
CALL IRQ_VECT
CLI
MOV AH,025H
LDS DX,CS:OLD_ISR10_ADDR
INT 21H ;Restore OLD vector
MOV AX,CS:SB_IRQ ;return with SB irq
POP CX
POP DX
POPF
RET
HANDLER2:
PUSH DX
PUSH AX
MOV DX,CS:SB_IO_ADDR
ADD DX,0EH
IN AL,DX
MOV CS:SB_IRQ,02
MOV AL,020H
OUT 20H,AL
POP AX
POP DX
IRET
HANDLER3:
PUSH DX
PUSH AX
MOV DX,CS:SB_IO_ADDR
ADD DX,0EH
IN AL,DX
MOV CS:SB_IRQ,03
MOV AL,020H
OUT 20H,AL
POP AX
POP DX
IRET
HANDLER5:
PUSH DX
PUSH AX
MOV DX,CS:SB_IO_ADDR
ADD DX,0EH
IN AL,DX
MOV CS:SB_IRQ,05
MOV AL,020H
OUT 20H,AL
POP AX
POP DX
IRET
HANDLER7:
PUSH DX
PUSH AX
MOV DX,CS:SB_IO_ADDR
ADD DX,0EH
IN AL,DX
MOV CS:SB_IRQ,07
MOV AL,020H
OUT 20H,AL
POP AX
POP DX
IRET
HANDLER10:
PUSH DX
PUSH AX
MOV DX,CS:SB_IO_ADDR
ADD DX,0EH
IN AL,DX
MOV CS:SB_IRQ,10
MOV AL,020H
OUT 020H,AL
OUT 0A0H,AL
POP AX
POP DX
IRET
SBFINDIRQ ENDP
;Get the Sound Blaster DSP version
GET_DSP_VER PROC NEAR
PUSH CX
MOV CX,10
MOV DX,CS:SB_IO_ADDR
ADD DX,0CH
MOV AL,0E1H
CALL sbdspw
JNC A21
MOV CS:ERROR,3 ;DSP not responding
JMP L23
A21:
G1: CALL sbdsprt
CMP AL,0AAH ;Fix error in reading version
JNZ G2
LOOP G1
MOV CS:ERROR,4 ;DSP error not responding or no version
JMP L23
G2: MOV AH,AL
CALL sbdsprt
MOV CS:SB_VER,AX
MOV DX,CS:SB_IO_ADDR
ADD DX,0CH
MOV AL,0E1H
CALL sbdspw
JNC A22
MOV CS:ERROR,3 ;DSP not responding
JMP L23
A22:
CALL sbdsprt
MOV AH,AL
CALL sbdsprt
CMP CS:SB_VER,AX
JE L23
MOV AX,00
MOV CS:SB_VER,AX
L23:
POP CX
RET
GET_DSP_VER ENDP
; Test Sound Blaster DMA transfer
TEST_DMA PROC NEAR
MOV DX,CS
LEA AX,TESTBUFFER
CALL PNT_TO_DMA
PUSH AX
PUSH DX ;SAVE CONVERTED BUFFER ADDRESS
MOV AL,0FFH
OUT 00CH,AL ;Clear byte pointer flip/flop
JMP $+2
MOV AX,CS:SB_DMA
ADD AL,04
OUT 0AH,AL ;Set mask bit to channel
JMP $+2
MOV AL,048H
ADD AX,CS:SB_DMA
OUT 0BH,AL ;Single mode select read channel
JMP $+2
MOV BX,CS:SB_DMA
MOV DX,0
MOV DL,CS:DMA_PAGE[BX]
POP BX ;Restore converted buffer address
MOV AL,BL
OUT DX,AL ;Set DMA page register
JMP $+2
POP AX
MOV DX,CS:SB_DMA ;Determine memory address register
SHL DX,1
OUT DX,AL ;Set base and current address
JMP $+2
MOV AL,AH
OUT DX,AL
JMP $+2
MOV BX,AX
XOR BX,-1 ;(65536-BX) = # BYTES THAT CAN BE TRANSFERED
MOV AX,0 ;# samples - 1
CMP AX,BX
JBE L100
MOV AX,BX
L100:
CLI
INC DX
OUT DX,AL ;Base & current word count
JMP $+2
MOV AL,AH
OUT DX,AL
JMP $+2
STI
MOV AX,CS:SB_DMA
OUT 0AH,AL ;Clear channel 1 mask bit to start DMA
JMP $+2
MOV DX,CS:SB_IO_ADDR
ADD DX,0CH
MOV AL,040H
CALL sbdspw ;Set DSP time constant
JNC A31
MOV CS:ERROR,3 ;DSP not responding
JMP A35
A31:
MOV AL,064H
CALL sbdspw
JNC A32
MOV CS:ERROR,3 ;DSP not responding
JMP A35
A32:
MOV AL,014H
CALL sbdspw ;DMA mode 8-bit DAC
JNC A33
MOV CS:ERROR,3 ;DSP not responding
JMP A35
A33:
MOV AX,0000 ;Number of bytes to be sent to DSP is 1
CALL sbdspw ;#samples to be sent to DSP
JNC A34
MOV CS:ERROR,3 ;DSP not responding
JMP A35
A34:
MOV AL,AH
CALL sbdspw
JNC A35
MOV CS:ERROR,3 ;DSP not responding
A35:
RET
TEST_DMA ENDP
; Halt DMA transfer
HALT_DMA PROC NEAR
MOV AX,CS ;Set DS to CS
MOV DS,AX
MOV AX,0001
CMP CS:DMAFLAG,0 ;Is DMAFLAG for DAC still in Use?
JE H_END ;Wait until done
CMP CS:DSP_SPEED,01 ;In high speed?
JNZ H_RESET
CALL DSP_RESET ;In high speed DSP must be reset first
H_RESET:
CALL STOP_DMA
CALL RESTORE ;Restore IRQ, etc.
H_END: RET
HALT_DMA ENDP
DSP_RESET PROC NEAR
MOV DX,CS:SB_IO_ADDR
ADD DL,06
MOV AL,01
OUT DX,AL
IN AL,DX
IN AL,DX
IN AL,DX
IN AL,DX
SUB AL,AL
OUT DX,AL
MOV BL,10H
D_GET: CALL sbdsprt
CMP AL,0AAH
JZ D_END
DEC BL
JNZ D_GET
MOV AX,0002 ;I/O read/write fail
STC
JMP D_END1
D_END: SUB AX,AX
D_END1: OR AX,AX
RET
DSP_RESET ENDP
STOP_DMA PROC NEAR
PUSHF
PUSH SI
MOV AH,0D0H ;HALT DMA IN PROGRESS
MOV BX, OFFSET DMAFLAG
SUB CX,CX
MOV DX,CS:SB_IO_ADDR
ADD DL,0CH
MOV SI,0FFFFH
S1: STI
CMP CL,[BX] ;Is dsp in use?
JZ SE
CLI
IN AL,DX
OR AL,AL
JS S2
DEC SI
JNZ S1
S5: MOV CS:ERROR,3 ;DSP error (not responding)
JMP SE
S2: MOV SI,0FFFFH ;Timeout Period
S3: DEC SI
JZ S5
IN AL,DX
OR AL,AL
JS S3
MOV AL,AH
OUT DX,AL
SE: POP SI
POPF
RET
STOP_DMA ENDP
RESTORE PROC NEAR
MOV AL,04 ;DMA channel mask register
ADD AX,CS:SB_DMA
OUT 0AH,AL
MOV AX,0
MOV CS:DMAFLAG,AX ;Indicate it is
LDS DX,CS:OLD_INTERRUPT_ADDR
MOV AX,CS:SB_IRQ
CALL IRQ_VECT
CLI
MOV AH,025H ;Restore old IRQ 5
INT 21h
CLI
MOV AX,CS:SB_IRQ ;Restore PICs
AND AL,08
JZ R10
MOV AL,CS:SAVEPIC
OUT 021H,AL
MOV DX,00A1H
JMP R11
R10: MOV DX,0021H
R11: MOV AL,CS:SAVEPIC2
OUT DX,AL
MOV DX,CS:SB_IO_ADDR ;Acknowledge DSP interrupt
ADD DL,0EH
IN AL,DX
RET
RESTORE ENDP
_sbdelay PROC FAR
;
; _sbdelay FAR Procedure for C Written by John A. Ball January 9, 1994
;
; Causes a time delay using the Sound Blaster DSP
;
; int error sbdelay(char tc, int period);
;
PARMA1 EQU [BP+6] ;Time Constant
PARMB1 EQU [BP+8] ;Delay period
PUSH BP ;Save stack Frame
MOV BP,SP
PUSHF
PUSH DS ;Save registers
PUSH ES
PUSH DI
MOV AX,DGROUP ;Set DS = our data segment
MOV DS,AX
CMP CS:DMAFLAG,0 ;Is DMAFLAG for DAC still in Use?
JZ DELAY
MOV CS:ERROR,1 ;DMA in use error
JMP EXIT3
DELAY: CMP CS:SB_IRQ,2 ;See if there is a known valid IRQ
JE DELAY1
CMP CS:SB_IRQ,3
JE DELAY1
CMP CS:SB_IRQ,5
JE DELAY1
CMP CS:SB_IRQ,7
JE DELAY1
CMP CS:SB_IRQ,10
JE DELAY1
CALL SBFINDIRQ ;Else find the IRQ
MOV CS:SB_TEST,1
CMP CS:ERROR,0 ;Check to see if IRQ was found
JE DELAY1
JMP EXIT3
DELAY1:
CALL INSTALL_ISR ;Install ISR for Sound Blaster IRQ & PIC
MOV CX,PARMA1 ;GET TIME CONSTANT
MOV BX,PARMB1 ;Get delay period
MOV DX,CS:SB_IO_ADDR
ADD DX,0CH
MOV AL,040H ;Set Time Constant
CALL sbdspw
MOV AL,CL
CALL sbdspw
MOV AL,080H ;Set Silence Length
CALL sbdspw
MOV AL,BL ;and send Period
CALL sbdspw
MOV AL,BH
CALL sbdspw
INC CS:DMAFLAG ;SET DMAFLAG TO SHOW IN USE
CALL IO_WAIT ; and wait for IRQ to occur
CALL RESTORE_ISR ; RESTORE ISR and PIC
EXIT3: MOV AX,CS:ERROR
POP DI ;Restore registers
POP ES
POP DS
POPF
POP BP ;RESTORE BP
RET
_sbdelay ENDP
GIVE_SLICE PROC NEAR
CMP CS:IN_DV,0 ;See if in DESQview?
JNE DV
INT 28H ;MS-DOS idle handler
MOV AX,1680H
INT 2FH ;MS-DOS idle call
JMP DV_EXIT
DV:
MOV AX,1000H ;Give up time slice to DESQview
INT 15H
DV_EXIT:
RET
GIVE_SLICE ENDP
FIND_DV PROC NEAR
PUSH BX
PUSH CX
PUSH DX
MOV CX,'DE'
MOV DX,'SQ'
MOV AX,2B01H
INT 21H
CMP AL,0FFH
JE NO_DV
MOV AX,BX
MOV CS:IN_DV,1
JMP E_F
NO_DV:
MOV AX,0
E_F:
POP DX
POP CX
POP BX
RET
FIND_DV ENDP
SET_VOLUME PROC NEAR
MOV AX,CS:SB_VER ;Get DSP version and see if card has mixer
CMP AX,0300H ;Versions 3 and above have mixer chip
JL EXIT_VOL
MOV AX,CS:VOLUME ;Get volume and convert to mixer format
MOV CX,4
MOV BX,AX
SHL AX,CL
ADD BX,AX
MOV DX,CS:SB_IO_ADDR
ADD DX,04H
MOV AX,04H ;Voice Volume Register
OUT DX,AL
INC DX
MOV AX,BX ;Get volume and set
OUT DX,AL
EXIT_VOL:
RET
SET_VOLUME ENDP
SET_MODE PROC NEAR
MOV AX,CS:STEREO ;Get mono or stereo mode
CMP AX,CS:PRE_MODE ;Same mode as before?
JE SM_EXIT
CMP CS:SB_VER,0300H ;Sound Blaster Pro
JL SM_EXIT
CMP CS:SB_VER,0400H ;only
JGE SM_EXIT
MOV DX,CS:SB_IO_ADDR
ADD DX,4
MOV CS:PRE_MODE,AX ;Save current mode
CMP AX,2 ;Stereo or mono
JE SET_STEREO
MOV AL,00CH ;Restore filter status
OUT DX,AL
INC DX
MOV AX,CS:OUT_FILTER
OUT DX,AL
DEC DX
MOV AL,00EH ;Set hardware to mono mode
OUT DX,AL
INC DX
IN AL,DX
AND AL,0FDH
OUT DX,AL
JMP SM_EXIT
SET_STEREO:
MOV AX,00EH ;Set hardware to stereo mode
OUT DX,AL
INC DX
IN AL,DX
OR AL,002H
OUT DX,AL
DEC DX
MOV AX,00EH
OUT DX,AL
INC DX
IN AL,DX
MOV CS:OUT_FILTER,AX
OR AL,020H
OUT DX,AL
SM_EXIT:
RET
SET_MODE ENDP
_TEXT ENDS
END